﻿using System.Collections.Generic;
using System.Linq;
using HazeSearch.Trie;

namespace HazeSearch.InvertedIndexing
{
    public static class TextSearch<TTarget>
    {
        public static ISet<TTarget> SearchSingleWord(string word, WordMapping<TTarget> mapping)
        {
            var index = 0;
            var included = new HashSet<string>();
            var nodes = new List<Node<char, WordInverted<TTarget>>>();
            var flippedNodes = new List<Node<char, WordInverted<TTarget>>>();
            var result = new HashSet<TTarget>();

            for (;index < word.Length;)
            {
                NodeContainer<char, WordInverted<TTarget>> nc = mapping.Trie;
                var iStart = index;
                var len = 0;
                for (; index < word.Length; index++, len++)
                {
                    var ch = word[index];
                    if (nc.SubNodes.ContainsKey(ch))
                    {
                        nc = nc.SubNodes[ch];
                    }
                    else
                    {
                        break;
                    }
                }
                var node = (Node<char, WordInverted<TTarget>>)nc;

                var ss = word.Substring(iStart, len);
                var contained = included.Any(s => s.Contains(ss));
                if (contained) continue;
                included.Add(ss);
                if (node.Value.Flipped)
                    flippedNodes.Add(node);
                else
                    nodes.Add(node);
            }

            foreach (var target in nodes.SelectMany(node => node.Value.Targets))
            {
                result.Add(target);
            }

            foreach (var node in flippedNodes)
            {
                foreach (var target in node.Value.Targets)
                {
                    if (result.Contains(target))
                    {
                        result.Remove(target);
                    }
                }
            }

            return result;
        }
    }
}
